#include "GameD3D.h"

GameD3D::GameD3D(LPCSTR title)
	:Game(title)
{
	//create d3d object
	printf("create d3d object...");
	if(!(d3d = Direct3DCreate9(D3D_SDK_VERSION)))
	{
		exit(-1);
	}
	printf("done.\n");

	//create device
	printf("create device...");
	DWORD behavior;
	D3DCAPS9 d3dcaps;
	ZeroMemory(&d3dcaps, sizeof(D3DCAPS9));
	if(FAILED(d3d->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dcaps)))
	{
		exit(-1);
	}
	if((d3dcaps.DevCaps&D3DDEVCAPS_HWTRANSFORMANDLIGHT)!=0)
	{
		if((d3dcaps.DevCaps&D3DDEVCAPS_PUREDEVICE)!=0)
		{
			behavior = D3DCREATE_HARDWARE_VERTEXPROCESSING|D3DCREATE_PUREDEVICE;
		}
		else
		{
			behavior = D3DCREATE_HARDWARE_VERTEXPROCESSING;
		}
	}
	else
	{
		behavior = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
	}
	D3DPRESENT_PARAMETERS d3dpp;
	d3dpp.BackBufferWidth				= SCREEN_WIDTH;						// client width
	d3dpp.BackBufferHeight				= SCREEN_HEIGHT;					// client height
	d3dpp.BackBufferFormat				= D3DFMT_X8R8G8B8;					// color format
	d3dpp.BackBufferCount				= 1;								// 1 back buffer
	d3dpp.MultiSampleType				= D3DMULTISAMPLE_NONE;				// (default) un-supported
	d3dpp.MultiSampleQuality			= 0;								// (default) un-supported
	d3dpp.SwapEffect					= D3DSWAPEFFECT_DISCARD;			// entire client repaint required
	d3dpp.hDeviceWindow					= hwnd;								// set to focus
	d3dpp.Windowed						= TRUE;								// window app
	d3dpp.EnableAutoDepthStencil		= TRUE;								// auto depth-stencil buffer
	d3dpp.AutoDepthStencilFormat		= D3DFMT_D24S8;						// depth-stencil buffer format
	d3dpp.Flags							= 0;								// (default)
	d3dpp.FullScreen_RefreshRateInHz	= D3DPRESENT_RATE_DEFAULT;			// (default) window app must be 0
	d3dpp.PresentationInterval			= D3DPRESENT_INTERVAL_IMMEDIATE;	// (default)
	if(FAILED(d3d->CreateDevice(
		D3DADAPTER_DEFAULT,	// use default adapter
		D3DDEVTYPE_HAL,		// use hal
		hwnd,				// focus window
		behavior,			// use checked vertex processing
		&d3dpp,				// present parameter
		&d3ddev				// device
	)))
	{
		exit(-1);
	}
	printf("done.\n");

	//create effects
	HR(D3DXCreateEffectFromFile(d3ddev, "MLP.fx", 0, 0, D3DXSHADER_DEBUG, 0, &dxfx, &dxbuf));
	if(dxbuf)
	{
		printf("%s\n",(char*)dxbuf->GetBufferPointer(), 0, 0);
		dxbuf->Release();
	}

}

GameD3D::~GameD3D()
{

}

void GameD3D::Redraw()
{
	HR(d3ddev->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, 0xFF000000, 1.0f, 0));
	
	HR(d3ddev->BeginScene());

	geos.foreach([=](Geometry* geo)
	{
		D3DXMATRIX mWorldIT;
		D3DXMatrixInverse(&mWorldIT,NULL,&geo->mWorld);
		D3DXMatrixTranspose(&mWorldIT,&mWorldIT);
		D3DXMATRIX mView;
		D3DXMatrixLookAtLH(&mView,&v3ViewEye,&v3ViewTarget,&v3ViewUp);

		HR(dxfx->SetTechnique("MultiLightPass"));

		HR(dxfx->SetMatrix("mW",&geo->mWorld));
		HR(dxfx->SetMatrix("mWIT",&mWorldIT));
		HR(dxfx->SetMatrix("mWVP", &(geo->mWorld*mView*mProj)));
		HR(dxfx->SetValue("f3VPos",&v3ViewEye,sizeof(D3DXVECTOR3)));

		HR(dxfx->SetVector("f4AM",&geo->v4AM));
		HR(dxfx->SetVector("f4DM",&geo->v4DM));
		HR(dxfx->SetVector("f4SM",&geo->v4SM));
		HR(dxfx->SetFloat("fSP",geo->fSP));

		HR(dxfx->Begin(NULL, 0));

		auto DrawMesh = [=](int idx){
			HR(dxfx->BeginPass(idx));
			HR(geo->dxm->DrawSubset(0));
			HR(dxfx->EndPass());
		};

		HR(dxfx->SetVector("f4AI",&v4DLAI));
		HR(dxfx->SetVector("f4DI",&v4DLDI));
		HR(dxfx->SetVector("f4SI",&v4DLSI));
		HR(dxfx->SetValue("f3LDir",&v3DLDir,sizeof(D3DXVECTOR3)));
		DrawMesh(0);

		d3ddev->SetRenderState(D3DRS_ZWRITEENABLE,FALSE);
		d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
		d3ddev->SetRenderState(D3DRS_BLENDOP,D3DBLENDOP_ADD);
		d3ddev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
		d3ddev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
		points.foreach([=](PointLight* pl)
		{
			HR(dxfx->SetVector("f4AI",&pl->v4AI));
			HR(dxfx->SetVector("f4DI",&pl->v4DI));
			HR(dxfx->SetVector("f4SI",&pl->v4SI));
			HR(dxfx->SetValue("f3LPos",&pl->v3Pos,sizeof(D3DXVECTOR3)));
			HR(dxfx->SetFloat("fA0",pl->v3Att.x));
			HR(dxfx->SetFloat("fA0",pl->v3Att.y));
			HR(dxfx->SetFloat("fA0",pl->v3Att.z));
			DrawMesh(1);
		});
		d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
		d3ddev->SetRenderState(D3DRS_ZWRITEENABLE,TRUE);

		HR(dxfx->End());
	});

	HR(d3ddev->EndScene());

	HR(d3ddev->Present(NULL,NULL,NULL,NULL));

}
